import cv2
import gc
import numpy as np
import torch
import locale
import time
import torch.backends.cudnn as cudnn
from PIL import Image, ImageDraw, ImageFont
from interval import Interval
import util
import uuid as u
from models.common import DetectMultiBackend
from utils.general import non_max_suppression, scale_coords, xyxy2xywh
from utils.torch_utils import select_device
from threading import Thread
from utils.dataloaders import LoadStreams
import cv2 as cv
import queue
import os
import sys
from pathlib import Path
from bytetrack_tracker.byte_tracker import BYTETracker
from plots import colors
import socket
from util import VideoCapture
from concurrent.futures import ThreadPoolExecutor,wait
from nvjpeg import NvJpeg
import schedule
import matplotlib
from util import sendMessage
matplotlib.use('TkAgg')  # 或者使用 'QtAgg'
import matplotlib.pyplot as plt
import time
import datetime
import configparser
import threading
BASE_DIR = os.path.dirname(os.path.abspath(__file__))

FILE = Path(__file__).resolve()
ROOT = FILE.parents[0]  # YOLOv5 root directory
if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT))  # add ROOT to PATH
os.environ["OMP_NUM_THREADS"] = "1"
os.environ["OPENBLAS_NUM_THREADS"] = "1"
os.environ["MKL_NUM_THREADS"] = "1"
os.environ["VECLIB_MAXIMUM_THREADS"] = "1"
os.environ["NUMEXPR_NUM_THREADS"] = "1"
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'

config = configparser.ConfigParser()
config.read(os.path.join(BASE_DIR, "config.ini"), encoding='UTF-8')
secs = config.sections()



def get_host_ip():
    """
    查询本机ip地址
    :return: ip
    """
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(('8.8.8.8', 80))
        ip = s.getsockname()[0]
    finally:
        s.close()
    return ip


# 查找字典中是否有指定的key值
def check_key_in_dict(dictionary, keyword):
    if len(dictionary) == 0:
        return False
    for key in dictionary.keys():
        if keyword == key:
            return True
    return False


# 暂存区存储老师状态
def teacher_status(points):
    # g =
    pass

# 线程池重构
class processThreadPool(ThreadPoolExecutor):
    """
    重写线程池修改队列数
    """
    def __init__(self, max_workers=None, thread_name_prefix=''):
        super().__init__(max_workers, thread_name_prefix)
        # 队列大小为最大线程数的两倍
        self._work_queue = queue.Queue(self._max_workers * 2)

class processBoxImg(ThreadPoolExecutor):
    """
    重写线程池修改队列数
    """
    def __init__(self, max_workers=None, thread_name_prefix=''):
        super().__init__(max_workers, thread_name_prefix)
        # 队列大小为最大线程数的两倍
        self._work_queue = queue.Queue(self._max_workers * 2)


class GenModule:
    def __init__(self, src_list, weight, params=None, stu_port=0, tea_port=0,
                 fps_show=False, track=False, msg_send=True, imagesize=640, half=False, camera_list=None, Time_time=10,
                 device=None, COUNTS=5, FramesListening=1, FramesPeople=1,
                 conf_thres=0.45, iou_thres=0.25,shareList=[],port="",person_type=None):
        self.sort = track  # 是否跟踪
        self.half = half  # 半精度计算
        self.port=5000
        self.person_type=person_type
        self.stu_port = stu_port  # 前摄像头端口号
        self.tea_port = tea_port  # 后摄像头端口号
        self.augment = True  #
        self.classes = None  #
        self.agnostic_nms = False  #
        self.src_list = src_list  # 视频源
        self.src_num = len(self.src_list)  # 视频源的数量
        self.TMPPoints = [[] for x in range(self.src_num)]  #
        self.TMPPoints_tmp = [{} for x in range(self.src_num)]
        self.people = [[] for x in range(self.src_num)]  #
        self.Listening = [[] for x in range(self.src_num)]  #
        self.ReadWrite = [[] for x in range(self.src_num)]  #
        self.camera_list = camera_list
        self.COUNTS = COUNTS  #
        self.FramesListening = FramesListening * 60 * 15  # ? 教室人数，间隔一分钟
        self.FramesPeople = FramesPeople * 60 * 15   # ? 教室人数，间隔一分钟
        self.device, self.deice_list = select_device(device, batch_size=self.src_num)
        # 资源数量
        self.Level2ActionNumberList = [[0, 0, 0, 0, 0] for j in range(
            self.src_num)]  ## PearCode 对应['L', 'NL', 'hand', 'stand', 'smoke']的动作次数  => ["L","NL","hand","stand","sleep"]
        self.Time_i = 0  # 抽帧计时
        self.Time_time = Time_time  # 隔多少帧检测一次

        # 视频流队列
        # self.q = queue.Queue(maxsize=1000)
        # self.q_list=[self.q for j in range(self.src_num)]
        # 5分钟定时器
        self.schdule_tea = schedule.every(5).minutes.do(self.schedule_five_minutes_send_tea_guiji)  # 教师轨迹推送5分钟定时器
        self.schdule_tea_photo = schedule.every(5).minutes.do(self.schdule_tea_send_photo)  # 教师轨迹图片推送定时器
        self.schdule_tea_visitation = schedule.every(1).minutes.do(self.schdule_tea_send_visitation)  # 教师巡视推送定时器
        self.send_tea_guiji = [False for j in range(self.src_num)]
        self.send_tea_photo = [False for j in range(self.src_num)]

        # 教师板书，讲授，师生互动1秒种定时器
        self.schdule_tea_tw = schedule.every(1).seconds.do(self.schdule_tea_send_tw)
        self.schdule_tea_lecture = schedule.every(1).seconds.do(self.schdule_tea_send_lecture)
        self.schdule_tea_TSI = schedule.every(1).seconds.do(self.schdule_tea_send_TSI)
        self.send_tea_tw = [False for j in range(self.src_num)]
        self.send_tea_lecture = [False for j in range(self.src_num)]
        self.send_tea_TSI = [False for j in range(self.src_num)]
        self.send_visitation = [False for j in range(self.src_num)]
        # 每天早上6点到晚上11点执行报警事件的定时器
        self.Level2sendSwitch=False  #开关
        self.teacher_mess_switch=False
        # 消息发送的线程池
        # self.max_workers=2 if self.front_person_type else 5
        # print(self.max_workers)
        # self.thread_pool=ThreadPoolExecutor(max_workers=self.max_workers)
        self.process_pool = processThreadPool(max_workers=8)
        # 一级报警触发器
        self.last_triggered = [{} for j in range(self.src_num)]  # 存储每种事件上次触发的时间
        self.window = [[] for j in range(self.src_num)]  # 滑动窗口，存储最近的事件
        self.switch_window = [{'fight': True, 'fall': True, 'fire': True, 'gather': True} for j in range(self.src_num)]
        # self.threshold = int(config.get("server", "level_1_alarm_threshold"))  # 触发报警的阈值
        self.threshold = {'fight': config.get("server", "level_1_alarm_fight_threshold"),
                          'fall': config.get("server", "level_1_alarm_fall_threshold"),
                          'fire': config.get("server", "level_1_alarm_fire_threshold"),
                          'gather': config.get("server", "level_1_alarm_gather_threshold")}
        self.trigger_img = [{'fight': [], 'fall': [], 'fire': [], 'gather': []} for j in range(self.src_num)]
        self.first_trigger_img = [{} for j in range(self.src_num)]

        self.switch_video = [{} for j in range(self.src_num)]
        self.switch = [{'fight': None, 'fall': None, 'fire': None, 'gather': None} for j in range(self.src_num)]
        self.send_switch = [{'fight': None, 'fall': None, 'fire': None, 'gather': None} for j in range(self.src_num)]
        self.num_list = [{'fight': 0, 'fall': 0, 'fire': 0, 'gather': 0} for j in range(self.src_num)]
        self.trigger_save_img = [{'fight': True, 'fall': True, 'fire': True, 'gather': True} for j in
                                 range(self.src_num)]
        self.have_save_video = [{'fight': False, 'fall': False, 'fire': False, 'gather': False} for j in
                                range(self.src_num)]
        self.first_level_alarm_time_interval = int(config.get("server", "first_level_alarm_time_interval"))
        self.level_1_alarm_frame_count = int(config.get("server", "level_1_alarm_frame_count"))
        self.check=[False for j in range(self.src_num)]

        #二级警报控制器
        self.Level2_num_list = [{'sleep': 0, 'phone': 0, 'smoke': 0, 'hand': 0, 'stand': 0} for j in
                                range(self.src_num)]


        self.x0 = 0  # 中心点坐标x,y
        self.y0 = 0
        self.i = 0  #
        # kandy ==>
        self.shareList = shareList
        self.sleeptime = 0
        self.teacher_trajectory = "tea_trajectory"
        self.residenceTime = 0  # 教师停留时间
        self.send_stu_message_lock = threading.Lock()
        self.send_tea_message_lock = threading.Lock()
        self.tea_guiji = threading.Lock()
        self.lock_th = threading.Lock()
        self.alarm_switch = [False for j in range(self.src_num)]
        self.wait_switch = [False for j in range(self.src_num)]
        self.wait_Time = [self.Time2FPS2(1) for j in range(self.src_num)]
        self.fight_num = 0
        self.fight_list = []
        self.people_num = 0
        # self.img_num = 0
        self.det = []
        self.teacherID = 9999
        self.all_stand_points = [{} for x in range(self.src_num)]
        self.right_lower_points = [{} for x in range(self.src_num)]
        self.teacherRightLowerPoints = {"teacherPoint": []}
        self.frames = 0
        self.visitation_time_control = 0  # 教师巡视，间隔30帧推一次
        self.class_time = 1 * 30 * 30  # 1帧 * 30 * 60 * 45 分钟
        self.class_time_control = self.class_time  # 教师轨迹图片控制 一节课一张
        self.tea_trajectory_time = 30 * 15  # 30 * 60 * 5 # 教师轨迹控制，5分钟一次  30 * 15
        self.tea_trajectory_control = self.tea_trajectory_time  # 五分钟
        self.stu_dw_time = 30 * 1  # 读写推送时间
        self.stu_dw_control = self.stu_dw_time
        self.tea_tsi_time = 30 * 1
        self.tea_tsi_control = self.tea_tsi_time
        self.tea_leature_time = 30 * 1
        self.tea_leature_control = self.tea_leature_time
        self.tea_tw_time = 30 * 1
        self.tea_tw_control = self.tea_tw_time
        self.start = 0
        self.end = 0
        self.front = "classroom_front"
        self.back = "classroom_back"
        self.nj = NvJpeg()
        # 传入参数 → 自身属性
        # 视频资源
        # 权重
        self.weight = weight if isinstance(weight, str) else weight[0]
        # 传入的警报类型，用于后续的box标注、保存视频名称
        # 警报参数
        self.params = [params for x in range(self.src_num)]
        # fps显示开关
        self.fps_show = fps_show
        # 目标跟踪
        self.track = track
        # 发送警报信息开关
        self.msg_send = msg_send
        # 放入模型的图片尺寸
        self.img_size = imagesize
        # 人员类型
        # 加载权重
        self.model = DetectMultiBackend(weight, device=self.device, fp16=half)  #
        # self.names = self.model.names if hasattr(self.model, 'model') else self.model.names  #pytorch模型
        if config.get("server", "show_names")=='True':
            self.show_names=True
        else:
            self.show_names=False
        if self.person_type == 1:
            self.names = ["L", "NL", "sleep", "stand", "hand", "fight", "phone", "gather", "fall", "DW", "fire",
                          "smoke"]
        else:
            self.names = ["sit", "stand", "TW", "fire", "smoke"]
        # self.names = ["L","NL","sleep","stand","hand","fight","phone","gather","fall","DW","fire","smoke"] if self.front_person_type else ["sit","stand","smoke","fire","TW"]
        # 跟踪
        if track:
            self.bytetrack_list = []
            # self.host = 5005
            for i in range(self.src_num):
                self.bytetrack_list.append(BYTETracker(track_thresh=0.5, track_buffer=70, match_thresh=0.8))
            self.outputs = [None] * self.src_num
        self.conf_thres = conf_thres
        self.iou_thres = iou_thres
        # 不同模型使用不同置信度阈值，可通过set_conf函数修改
        # 判定track的参数

        self.sort_id = [[] for x in range(self.src_num)]
        self.k_time = [0] * self.src_num
        self.current_id = [9999] * self.src_num

        self.points = [[] for x in range(self.src_num)]
        # 多线程视频流及缓存池
        # 违规行为前的视频时间
        # 视频流的实时画面和缓存池
        self.dataset = LoadStreams(self.src_list, self.img_size,self.camera_list)
        # 视频流的fps列表，用于设置VideoWriter保存视频的帧率

        # 视频保存帧的列表
        self.images = {}
        for i in range(self.src_num):
            self.images[i] = []

        self.class_type = [99 for j in range(self.src_num)]  # 违规标签的类型
        self.class_name = ["" for j in range(self.src_num)]  # 违规标签的名字
        self.vid_writer = [None] * self.src_num
        # 违规判定相关
        # 应用于：违规开始时的连续帧检测和违规过程中的间断帧干扰

        # 视频保存相关
        # 各个流当前所打开的VideoWriter的name
        self.videoWriter_name = ['' for j in range(self.src_num)]
        # 返回网页显示画面
        # 对img1s画框不会对视频存储过程中的画面造成影响，
        self.camera_map = {}
        # 如需在保存视频中画框，请在SubFunction子功能中调用results_process时传入boxes参数
        self.img1s = [] * self.src_num
        self.class_is = 0  # 判断是否上下课

        self.resutl_path = '/D:/ketang/flask_ketang_huizong_xiugai_mulgpu/'
        self.ip_stu = 'http://' + get_host_ip() + ':' + str(self.port)
        self.ip_tea = 'http://' + get_host_ip() + ':' + str(self.port)
        self.cu_time=0
        # self.push_thread=Thread(target=push_frame,args=(self.img1s[self.i].shape[0],self.img1s[self.i].shape[1],self.q_list[self.i],self.camera_list[self.i]))
    def schedule_five_minutes_send_tea_guiji(self):
        self.send_tea_guiji = [True for j in range(self.src_num)]

    def schdule_tea_send_tw(self):
        self.send_tea_tw = [True for j in range(self.src_num)]

    def schdule_tea_send_lecture(self):
        self.send_tea_lecture = [True for j in range(self.src_num)]

    def schdule_tea_send_TSI(self):
        self.send_tea_TSI = [True for j in range(self.src_num)]

    def schdule_tea_send_photo(self):
        self.send_tea_photo = [True for j in range(self.src_num)]

    def schdule_tea_send_visitation(self):
        self.send_visitation = [True for j in range(self.src_num)]
    def trigger_alarm(self, cameraID, events, img):
        current_time = time.time()
        # 只处理 'fight', 'fall', 'fire', 'gather' 四种事件
        valid_events = [event for event in events if event in ['fire','fight', 'fall', 'gather']]
        # print(valid_events)
        for event in self.send_switch[cameraID]:
            # print("事件==={}资源大小==={}：".format(event,len(self.trigger_img[cameraID][event])))

            if self.num_list[cameraID][event] != 0:    #num_list记帧数，当出现这一路的该事件时开始计帧，接下来的20帧内少于10帧出现该事件则为误判
                self.num_list[cameraID][event] += 1
            if self.send_switch[cameraID][event] == True:
                if len(self.trigger_img[cameraID][event]) == 150 and self.switch[cameraID][event]:
                    print("evnet====={}5秒到了,开始保存视频".format(event))
                    self.send_switch[cameraID][event] = False
                    self.switch_video[cameraID][event] = None
                    self.trigger_save_img[cameraID][event] = False
                    self.have_save_video[cameraID][event] = True
                    return True, event, self.trigger_img[cameraID][event][0], self.trigger_img[cameraID][event], False
            # 检查滑动窗口中是否有足够的事件触发报警
            if self.num_list[cameraID][event] == self.level_1_alarm_frame_count and self.window[cameraID].count(event) >= int(self.threshold[event]) and \
                    self.switch_window[cameraID][event]:
                # 清空窗口中的所有该事件
                self.window[cameraID] = [evt for evt in self.window[cameraID] if evt != event]
                self.switch[cameraID][event] = True
                self.send_switch[cameraID][event] = True
                self.switch_window[cameraID][event] = False
                self.num_list[cameraID][event] = 0

                # 触发报警
                print(f"触发 {event} 报警,摄像头为{cameraID}")
                self.last_triggered[cameraID][event] = current_time
                return True, event, self.first_trigger_img[cameraID][event], None, True
            # 漏检误检的情况
            if self.num_list[cameraID][event] == self.level_1_alarm_frame_count and self.window[cameraID].count(event) < int(self.threshold[event]) and \
                    self.switch_window[cameraID][event]:
                # print("误检漏检：",event)
                self.window[cameraID] = [evt for evt in self.window[cameraID] if evt != event]
                self.num_list[cameraID][event] = 0
                self.trigger_img[cameraID][event] = []
                if event in self.first_trigger_img[cameraID]:
                    del self.first_trigger_img[cameraID][event]
                # self.send_switch[cameraID][event]=False
            if event in self.last_triggered[cameraID] and current_time - self.last_triggered[cameraID][
                event] >= self.first_level_alarm_time_interval:  # 过了5分钟
                # print("时间到了：",self.switch_video[cameraID])
                del self.last_triggered[cameraID][event]
                self.switch_window[cameraID][event] = True
                del self.switch_video[cameraID][event]  # 时间到了5分钟，就删除对应事件的键值对，重新计时
                self.trigger_save_img[cameraID][event] = True
                self.have_save_video[cameraID][event] = False
            # 检查是否已经过了5分钟
            if event in self.last_triggered[cameraID] and current_time - self.last_triggered[cameraID][
                event] < self.first_level_alarm_time_interval:
                # print("时间没到：", self.switch_video[cameraID])
                if self.have_save_video[cameraID][event]:
                    self.trigger_img[cameraID][event] = []
                    if self.send_switch[cameraID][event] == False and event in self.first_trigger_img[cameraID]:
                        del self.first_trigger_img[cameraID][event]

        for event in valid_events:
            # 循环遍历字典的键，并将对应的值加1
            if self.num_list[cameraID][event] == 0:
                if self.switch_window[cameraID][event]:  # 还未发送报警信息时
                    self.num_list[cameraID][event] += 1
            # if self.switch_window[cameraID][event]:
            #     self.trigger_img[cameraID][event].append(img)
            # 保存第一帧图片
            if event not in self.first_trigger_img[cameraID]:
                self.first_trigger_img[cameraID][event] = img
            # 检查是否已经过了5分钟
            if event in self.last_triggered[cameraID] and current_time - self.last_triggered[cameraID][
                event] < self.first_level_alarm_time_interval:
                continue

            if event not in self.switch_video[cameraID]:
                self.switch_video[cameraID][event] = current_time
            if self.switch_window[cameraID][event]:
                # 更新滑动窗口
                self.window[cameraID].append(event)
        for key in self.trigger_img[cameraID]:
            if self.send_switch[cameraID][key]:
                self.trigger_img[cameraID][key].append(img)
        return False, None, None, None, False

    def teacher_guiji(self, cameraID, names, img):
        self.send_tea_message_lock.acquire()
        # self.send_tea_guiji[cameraID]=False
        # 推送消息
        self.params[cameraID]["camera_id"] = self.camera_list[int(cameraID)]
        self.params[cameraID]["location"] = self.back
        self.params[cameraID]["created_at"] = self.get_current_time()
        a = str(u.uuid1()).split("\(")[0]
        self.params[cameraID]["data_id"] = a
        self.params[cameraID]["event_level"] = 3  # 事件等级
        self.params[cameraID]["event_type"] = 35
        self.params[cameraID]["total_people"] = 1
        self.params[cameraID]["trajectory"] = self.TMPPoints[cameraID]
        self.params[cameraID]["screenshot_url"] = ""
        self.params[cameraID]["video_url"] = ""
        # print("id为：",cameraID)
        # print("开始长度为：", self.q_list[cameraID].qsize())
        # self.q_list[cameraID].put(self.params[cameraID])
        # print("消息：",self.params[cameraID])
        # print("目前长度为：",self.q_list[cameraID].qsize())
        # self.thread_pool.submit(sendMessage,self.params[cameraID])
        util.sendMessage(self.params[cameraID])
        self.send_tea_message_lock.release()
        # self.send_tea_guiji[cameraID]=False
        # print("长度为：",len(self.TMPPoints[cameraID]))
        # print("消息：",self.q.get())
        self.TMPPoints[cameraID] = []
        # self.send_tea_message_lock.release()

    def tea_tw(self, cameraID, names, img):
        self.send_tea_message_lock.acquire()
        # self.send_tea_tw[cameraID]=False
        # print("TW:",self.send_tea_tw)
        format_time = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime())
        camera_id = self.camera_list[int(cameraID)]
        forder_path = "static/tea_action"
        if not os.path.exists(forder_path):
            os.mkdir(forder_path)
        pic_path = 'Camera' + str(camera_id) + '-TW-' + format_time + ".png"
        save_img_name_path = forder_path + "/" + pic_path
        cv2.imwrite(save_img_name_path, img)
        picturePath = self.ip_tea + '/' + save_img_name_path
        # 推送消息
        self.params[cameraID]["camera_id"] = self.camera_list[int(cameraID)]
        self.params[cameraID]["location"] = self.back
        self.params[cameraID]["created_at"] = self.get_current_time()
        a = str(u.uuid1()).split("\(")[0]
        self.params[cameraID]["data_id"] = a
        self.params[cameraID]["event_level"] = 3  # 事件等级
        self.params[cameraID]["event_type"] = 38
        # print("----->1074:",self.people[cameraID])
        self.params[cameraID]["total_people"] = 1
        self.params[cameraID]["trajectory"] = []
        self.params[cameraID]["screenshot_url"] = picturePath
        self.params[cameraID]["video_url"] = ""
        # self.q_list[cameraID].put(self.params[cameraID])
        # self.thread_pool.submit(sendMessage,self.params[cameraID])
        util.sendMessage(self.params[cameraID])
        # self.tea_tw_control = self.tea_tw_time
        # self.send_tea_tw[cameraID]=False
        self.send_tea_message_lock.release()

    def tea_lecture(self, cameraID, names, img):
        self.send_tea_message_lock.acquire()
        # self.send_tea_lecture[cameraID]=False
        # print("lecture:",self.send_tea_lecture)
        format_time = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime())
        camera_id = self.camera_list[int(cameraID)]
        forder_path = "static/tea_action"
        if not os.path.exists(forder_path):
            os.mkdir(forder_path)
        pic_path = 'Camera' + str(camera_id) + '-leature-' + format_time + ".png"
        save_img_name_path = forder_path + "/" + pic_path
        cv2.imwrite(save_img_name_path, img)
        picturePath = self.ip_tea + '/' + save_img_name_path
        # 推送消息
        self.params[cameraID]["camera_id"] = self.camera_list[int(cameraID)]
        self.params[cameraID]["location"] = self.back
        self.params[cameraID]["created_at"] = self.get_current_time()
        a = str(u.uuid1()).split("\(")[0]
        self.params[cameraID]["data_id"] = a
        self.params[cameraID]["event_level"] = 3  # 事件等级
        self.params[cameraID]["event_type"] = 37
        # print("----->1074:",self.people[cameraID])
        self.params[cameraID]["total_people"] = 1
        self.params[cameraID]["trajectory"] = []
        self.params[cameraID]["screenshot_url"] = picturePath
        self.params[cameraID]["video_url"] = ""
        # self.q_list[cameraID].put(self.params[cameraID])
        # self.thread_pool.submit(sendMessage,self.params[cameraID])
        util.sendMessage(self.params[cameraID])
        # self.tea_leature_control = self.tea_leature_time
        # self.send_tea_lecture[cameraID]=False
        self.send_tea_message_lock.release()

    # 推送教师轨迹图片
    def tea_photo(self, cameraID, names, img):
        self.send_tea_message_lock.acquire()
        # print("前-----",self.send_tea_photo)
        # self.send_tea_photo[cameraID]=False
        # print("后-----",self.send_tea_photo)
        format_time = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime())
        camera_id = self.camera_list[int(cameraID)]
        forder_path = self.teacher_trajectory
        if not os.path.exists(forder_path):
            os.mkdir(forder_path)
        pic_path = 'Camera' + str(camera_id) + '-trajectory-' + format_time + ".png"
        save_img_name_path = "static/" + self.teacher_trajectory + "/" + pic_path
        self.save_tea_trajectory_img(cameraID, img, save_img_name_path)
        save_img_name_path = "static/" + self.teacher_trajectory + "/" + pic_path
        picturePath = self.ip_tea + '/' + save_img_name_path
        # 推送消息
        self.params[cameraID]["camera_id"] = self.camera_list[int(cameraID)]
        self.params[cameraID]["location"] = self.back
        self.params[cameraID]["created_at"] = self.get_current_time()
        a = str(u.uuid1()).split("\(")[0]
        self.params[cameraID]["data_id"] = a
        self.params[cameraID]["event_level"] = 3  # 事件等级
        self.params[cameraID]["event_type"] = 36
        self.params[cameraID]["total_people"] = 1
        self.params[cameraID]["trajectory"] = []
        self.params[cameraID]["screenshot_url"] = picturePath
        self.params[cameraID]["video_url"] = ""

        # self.q_list[cameraID].put(self.params[cameraID])
        # self.thread_pool.submit(sendMessage,self.params[cameraID])
        util.sendMessage(self.params[cameraID])
        # self.send_tea_photo[cameraID]=False
        self.send_tea_message_lock.release()
        # self.send_stu_message_lock.release()
        self.class_time_control = self.class_time

    def tea_TSI(self, cameraID, names, img):
        self.send_tea_message_lock.acquire()
        # self.send_tea_TSI[cameraID]=False
        # print("TSI:{},cameraID:{}".format(self.send_tea_TSI,cameraID))
        stand_num = 0
        for action in names:
            if action == "stand":
                stand_num += 1

        format_time = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime())
        camera_id = self.camera_list[int(cameraID)]
        forder_path = "static/tea_action"
        if not os.path.exists(forder_path):
            os.mkdir(forder_path)
        pic_path = 'Camera' + str(camera_id) + '-TSI-' + format_time + ".png"
        save_img_name_path = forder_path + "/" + pic_path
        cv2.imwrite(save_img_name_path, img)
        picturePath = self.ip_tea + '/' + save_img_name_path
        # 推送消息
        self.params[cameraID]["camera_id"] = self.camera_list[int(cameraID)]
        self.params[cameraID]["location"] = self.back
        self.params[cameraID]["created_at"] = self.get_current_time()
        a = str(u.uuid1()).split("\(")[0]
        self.params[cameraID]["data_id"] = a
        self.params[cameraID]["event_level"] = 3  # 事件等级
        self.params[cameraID]["event_type"] = 42
        self.params[cameraID]["total_people"] = 1
        self.params[cameraID]["trajectory"] = []
        self.params[cameraID]["screenshot_url"] = picturePath
        self.params[cameraID]["video_url"] = ""
        # self.q_list[cameraID].put(self.params[cameraID])
        # self.thread_pool.submit(sendMessage,self.params[cameraID])
        util.sendMessage(self.params[cameraID])
        # self.send_tea_message_lock.release()
        # self.send_tea_TSI[cameraID]=False
        self.tea_leature_control = self.tea_leature_time
        self.send_tea_message_lock.release()

    # 教师巡视
    def tea_visitation(self, cameraID, names, img):
        self.send_tea_message_lock.acquire()
        # self.send_tea_lecture[cameraID]=False
        # print("lecture:",self.send_tea_lecture)
        format_time = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime())
        camera_id = self.camera_list[int(cameraID)]
        forder_path = "static/tea_action"
        if not os.path.exists(forder_path):
            os.mkdir(forder_path)
        pic_path = 'Camera' + str(camera_id) + '-visitation-' + format_time + ".png"
        save_img_name_path = forder_path + "/" + pic_path
        cv2.imwrite(save_img_name_path, img)
        picturePath = self.ip_tea + '/' + save_img_name_path
        # 推送消息
        self.params[cameraID]["camera_id"] = self.camera_list[int(cameraID)]
        self.params[cameraID]["location"] = self.back
        self.params[cameraID]["created_at"] = self.get_current_time()
        a = str(u.uuid1()).split("\(")[0]
        self.params[cameraID]["data_id"] = a
        self.params[cameraID]["event_level"] = 3  # 事件等级
        self.params[cameraID]["event_type"] = 39
        # print("----->1074:",self.people[cameraID])
        self.params[cameraID]["total_people"] = 1
        self.params[cameraID]["trajectory"] = []
        self.params[cameraID]["screenshot_url"] = picturePath
        self.params[cameraID]["video_url"] = ""
        # self.q_list[cameraID].put(self.params[cameraID])
        # self.thread_pool.submit(sendMessage,self.params[cameraID])
        util.sendMessage(self.params[cameraID])
        # self.tea_leature_control = self.tea_leature_time
        # self.send_tea_lecture[cameraID]=False
        self.send_tea_message_lock.release()
    def get_current_time(self):
        current_time = datetime.datetime.now()
        formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S")
        return formatted_time

    # 判断当前主机是否断网
    def isNetChainOK(self, testserver):
        s = socket.socket()
        s.settimeout(3)
        try:
            status = s.connect_ex(testserver)
            if status == 0:
                s.close()
                return True
            else:
                return False
        except Exception as e:
            return False

    def judgeNet(self, testserver=('www.baidu.com', 443)):
        isOK = self.isNetChainOK(testserver)
        return isOK

    def preprocess(self, img):
        # img = np.ascontiguousarray(img)
        img = torch.from_numpy(img).to(self.device)
        img = img.float()  # uint8 to fp16/32
        img /= 255.0  # 图像归一化
        return img

    # 给图像分batch
    def batch_list(self, img_list, n):
        newimg = []
        for i in range(0, len(img_list), n):
            newimg.append(img_list[i:i + n])
        return np.ascontiguousarray(newimg)

    # -----------------------------2023-05-15-----------------------------------------
    # 检测图像
    def detect_stream(self,list_, resized_img, img0s):
        # start_time = time.time()
        with torch.no_grad():
            # 空运行，确保模型没问题

            # 数据预处理，将像素[0,255] --> [0,1]
            # cu_time = time.time()
            img_1 = self.preprocess(resized_img)
            # print(f"{self.port}预处理消耗时间：",time.time()-cu_time) #30路拉满的时候0.2左右


            #不做推理直接回显测试
            # for i,img in enumerate(img0s):
            #     Thread(target=self.save_img(i,img)).start()
            # start = time.time()
            preds = self.model(img_1,
                               augment=self.augment)  # batch:1,num_boxs:186843,num_classes+5,前4个是坐标信息，第5个是conf，后面5个分别为类别分数

            # self.cu_time = time.time()
            pred_1 = non_max_suppression(preds, self.conf_thres, self.iou_thres, classes=self.classes,
                                         agnostic=self.agnostic_nms)
            self.frames += 1
            process_list=[]
            # cu_time = time.time()
            for i, det in enumerate(pred_1):
                if i >= len(self.src_list):
                    break
                process=self.process_pool.submit(self.postProcessing,list_,i,det,img_1,img0s,resized_img)
                process_list.append(process)
                wait(process_list)

    # 执行后处理逻辑代码
    def postProcessing(self,list_,i,det,img_1,img0s,resized_img):
        if self.sort:  # 是否跟踪
            names = []
            in_podium_num = 0
            # 画分割线
            height = self.img1s[i].shape[0]
            width = self.img1s[i].shape[1]
            cv2.line(self.img1s[i], (550, 380), (width - 550, 380), (0, 0, 255, 200), 5)
            if det is not None and len(det) > 0:
                det[:, :4] = scale_coords(img_1.shape[2:], det[:, :4], img0s[i].shape).round()
                self.outputs[i] = self.bytetrack_list[i].update(
                    det[:, :6].cpu(), [img0s[i].shape[0], img0s[i].shape[1]], resized_img.shape)
                judge_time = int(config.get("server", "judge_time"))  # 判定帧数
                if len(self.outputs[i]) > 0:
                    flag = True
                    for output in self.outputs[i]:  # 遍历每个目标 reversed：反转/倒序迭代器
                        have_person = False
                        xyxy = (output.tlwh[0], output.tlwh[1], output.tlwh[0] + output.tlwh[2],
                                output.tlwh[1] + output.tlwh[3])
                        id = output.track_id
                        cls = output.cls.item()
                        x, y = (int(xyxy[2]), int(xyxy[3]))  # 右下角红色点的坐标
                        # 中心点坐标
                        x0 = int((int(xyxy[0]) + int(xyxy[2])) / 2)
                        y0 = int((int(xyxy[1]) + int(xyxy[3])) / 2)
                        self.x0 = x0
                        self.y0 = y0
                        self.i = i
                        center = (x0, y0)
                        names.append(self.names[int(cls)])
                        # 判断讲台区域是否有两个
                        if self.names[int(cls)] == 'stand':
                            if y <= 380:
                                in_podium_num += 1
                            if in_podium_num >= 2:
                                # print("讲台有两个人!!!")
                                names.append("TSI")  # 标签TS：师生互动
                        # new 讲台老师行为画框后处理
                        if self.names[int(cls)] == 'sit':
                            if y0 < 300:
                                self.boxed_img(xyxy, self.img1s[i], (0, 255, 0),self.names[int(cls)],show_names=self.show_names)

                        if self.names[int(cls)] == 'TW':
                            if y0 < 120:
                                self.boxed_img(xyxy, self.img1s[i], colors(int(cls)),self.names[int(cls)],show_names=self.show_names)

                        if self.names[int(cls)] == 'stand' or self.names[int(cls)] == 'smoke':  # new老师行为画框
                            # 画框
                            self.boxed_img(xyxy, self.img1s[i], colors(int(cls)),self.names[int(cls)],show_names=self.show_names)

                        # if self.names[int(cls)] == 'stand' and int(id) != self.teacherID:     old
                        # if self.names[int(cls)] == 'stand' or self.names[int(cls)] == 'sit' or self.names[
                        #     int(cls)] == 'smoke' or self.names[int(cls)] == 'TW':  # new老师行为画框
                        #     # 画框
                        #     self.boxed_img(xyxy, self.img1s[i], colors(int(cls)), self.names[int(cls)])
                        # cv2.circle(self.img1s[i], (x, y), 5, (0, 0, 255), -1)  # 边框右下角画红点    new:删除

                        if self.k_time[i] < judge_time and (
                                self.names[int(cls)] == 'stand' or self.names[int(cls)] == 'sit'):
                            res_center = check_key_in_dict(self.all_stand_points[i], str(int(id)))
                            res_right_lower = check_key_in_dict(self.all_stand_points[i], str(int(id)))
                            if res_center == False:
                                # 初始化字典
                                self.all_stand_points[i][str(int(id))] = []
                            if res_right_lower == False:
                                self.right_lower_points[i][str(int(id))] = []

                            # 暂存中心点
                            self.all_stand_points[i][str(int(id))].append(center)
                            # if (len(self.TMPPoints[i]) <= 1):
                            #     self.residenceTime = 0
                            # else:
                            #     self.residenceTime = time.time() - self.TMPPoints[len(self.TMPPoints[i]) - 1]["s"]
                            # self.TMPPoints_tmp[i][str(int(id))].append({"x": x0, "y": y0, "t": time.time(),"s": self.residenceTime})
                            # 暂存右下点
                            self.right_lower_points[i][str(int(id))].append((x, y))

                            self.sort_id[i].append(id)
                            if flag:
                                self.k_time[i] = self.k_time[i] + 1
                                flag = False
                        if self.k_time[i] == judge_time:
                            self.current_id[i] = np.argmax(np.bincount(self.sort_id[i]))
                            self.teacherID = self.current_id[i]

                            # 将暂存的中心点坐标加入到self.points[i]中，并清空字典
                            # 判断字典是否为空
                            if len(self.all_stand_points[i]) != 0:
                                self.points[i].extend(self.all_stand_points[i][str(self.teacherID)])
                                # self.TMPPoints[i].extend(self.TMPPoints_tmp[i][str(self.teacherID)])
                                self.all_stand_points[i].clear()
                                # self.TMPPoints[i].clear()
                            # 将暂存的中心点坐标加入到self.teacherRightLowerPoints中，并清空字典
                            if len(self.right_lower_points[i]) != 0:
                                self.teacherRightLowerPoints["teacherPoint"].extend(
                                    self.right_lower_points[i][str(self.teacherID)])
                                self.right_lower_points[i].clear()
                        # old
                        # if int(id) == self.teacherID and (
                        #         self.names[int(cls)] == 'stand' or self.names[int(cls)] == 'sit') or self.names[
                        #     int(cls)] == 'TW':
                        #     cv2.circle(self.img1s[i], (x, y), 5, (0, 0, 255), -1)  # 边框右下角画红点
                        #     have_person = True
                        #     if y > 380:  # 表示红色点超过了临界值，表示在巡视即为绿色框
                        #         self.boxed_img(xyxy, self.img1s[i], (0, 255, 0), self.names[int(cls)])
                        #         names.append("visitation")  # 巡视
                        #     else:
                        #         # 表示在板书或者讲授，即为黄色框
                        #         self.boxed_img(xyxy, self.img1s[i], (0, 255, 255), self.names[int(cls)])
                        #         names.append("lecture")  # 讲授
                        #     have_person = True
                        #     if self.frames % 20 == 0:
                        #         if (len(self.TMPPoints[i]) <= 1):
                        #             self.residenceTime = 0
                        #         else:
                        #             self.residenceTime = round(
                        #                 time.time() - self.TMPPoints[i][len(self.TMPPoints[i]) - 1]["t"], 2)
                        #         # self.addTMPPoints()
                        #         self.TMPPoints[i].append(
                        #             {"x": x0, "y": y0, "t": round(time.time(), 2), "s": self.residenceTime})
                        #         self.points[i].append(center)

                        # new
                        # if int(id) == self.teacherID and (self.names[int(cls)] == 'stand')or self.names[int(cls)] == 'TW':
                        if int(id) == self.teacherID and (self.names[int(cls)] == 'stand' or self.names[int(cls)] == 'sit' or self.names[int(cls)] == 'TW'):
                            cv2.circle(self.img1s[i], (x, y), 5, (0, 0, 255), -1)  # 边框右下角画红点
                            have_person = True
                            if y0 > 300:  # 表示红色点超过了临界值，表示在巡视即为绿色框
                                self.boxed_img(xyxy, self.img1s[i], (0, 255, 0))
                                names.append("visitation")  # 巡视
                            else:
                                # 表示在板书或者讲授，即为黄色框
                                self.boxed_img(xyxy, self.img1s[i], (0, 255, 255))
                                names.append("lecture")  # 讲授
                            have_person = True
                            if self.frames % 20 == 0:
                                if (len(self.TMPPoints[i]) <= 1):
                                    self.residenceTime = 0
                                else:
                                    self.residenceTime = round(
                                        time.time() - self.TMPPoints[i][len(self.TMPPoints[i]) - 1][
                                            "t"], 2)
                                # self.addTMPPoints()
                                self.TMPPoints[i].append({"x": x0, "y": y0, "t": round(time.time(), 2),
                                                          "s": self.residenceTime})
                                self.points[i].append(center)
                                # cv.circle(self.img1s[i], center, 1, (255, 0, 0), cv.FILLED)

                        # 将之前的点画在图像上
                        # for point in self.points[i]:
                        #     cv.circle(self.img1s[i], (point[0], point[1]), 1, (255, 255, 255),
                        #               cv.FILLED)
                        # if self.names[int(cls)] == 'F':
                        #     self.boxed_img(xyxy, self.img1s[i], colors(int(cls)), self.names[int(cls)])
                        if have_person == False and self.k_time[i] == judge_time:
                            self.sort_id[i].clear()
                            self.k_time[i] = 0
                            # 清空暂存区的坐标点
                            self.all_stand_points[i].clear()
                            # 清空右下角点暂存区的坐标点
                            self.right_lower_points[i].clear()
                    # if 'smoke' in names:
                    #     Thread(target=self.resutl_save(i, names,is_front=False)).start()
                # return_names.append(names)

            # self.save_img(list_, i)
            if self.dataset.caps_isopen[i]:
                if 'smoke' in names :
                    Thread(target=self.resutl_save(i, names, self.img1s[i], is_front=False)).start()
                if self.teacher_mess_switch:
                    Thread(target=self.send_tea_message, args=(i, names, self.img1s[i])).start()
        # 前摄像头
        else:
            # cu = time.time()
            names = []
            if det is not None and len(det) > 0:
                self.det = det
                # start = time.time()
                det[:, :4] = scale_coords((self.img_size, self.img_size), det[:, :4], img0s[
                    i].shape).round()  # scale_coords函数的作用是将预测所得到的结果进行转换。因为我们是基于resize之后的图片进行预测的，所以最开始得到的预测结果不是最终原图所对应的，该函数就是把相对应的标签进行转换为原图尺寸上。 --Pear
                # hua_time=time.time()
                for j, (*xyxy, conf, cls) in enumerate(det):  # 遍历每个目标 reversed：反转/倒序迭代器
                    if self.names[int(cls)] in['fall','gather','fight','fire']:
                        continue
                    self.boxed_img(xyxy, self.img1s[i], colors(int(cls)),self.names[int(cls)],show_names=self.show_names)
                    names.append(self.names[int(cls)])
                # print("111")

                # self.save_img(list_, i)
                if self.dataset.caps_isopen[i]:
                    Thread(target=self.resutl_save, args=(i, names, self.img1s[i])).start()  # 处理视频报警线程
        self.save_img(list_,i)

    def select_labels(self,xyxy,cls):
        xl, yl = (int(xyxy[0]), int(xyxy[1]))
        xr, yr = (int(xyxy[2]), int(xyxy[3]))
        area = (xr - xl) * (yr - yl)
        name = self.names[int(cls)]

        if name == 'L' or name == 'NL':
            if area <= 57000:
                return True
        # if self.names[int(cls)] == 'phone':
        if name == 'phone':
            if area <= 6500:
                return True
        # if self.names[int(cls)] == 'hand' or 'DW' or 'sleep':
        if name == 'hand' or name == 'DW' or name == 'sleep':
            if 6500<= area <= 35000:
                return True
        # if self.names[int(cls)] == 'stand' or 'fall':
        if name == 'stand' or name == 'fall':
            if area >= 10000:
                return True
        # if self.names[int(cls)] == 'gather' or 'fight':
        if name == 'gather' or name == 'fight':
            if 15000<= area <= 80000:
                return True


    # 不做推理直接回显测试代码
    # def save_img(self, i,img):
    #     if (i < len(self.src_list)):
    #         if self.dataset.caps[i].isOpened():
    #             if len(self.img1s) > 0:
    #                 param = str(self.camera_list[i]) + ":" + "name"
    #                 # encoder = self.nj.encode(self.img1s[i])
    #                 # time1=time.time()
    #                 encoder = self.nj.encode(img)
    #                 # print("正在处理第几个：",i)time
    #                 # print("时间消耗：",time.time()-time1)
    #                 self.camera_map["gas:video:" + param] = encoder
    #                 del encoder
    #
    #                 # ret, jpeg = cv2.imencode('.jpg', self.img1s[i])
    #                 # self.camera_map["gas:video:" + param] = jpeg.tobytes()
    #             else:
    #                 print("摄像头不在线它的camera_id是:", self.camera_list[int(i)])
    #                 cap = VideoCapture(self.src_list[i])
    #                 if cap == -1:
    #                     self.clear_variable(i)  # 摄像头掉线，改变算法状态
    #                 else:
    #                     self.dataset.caps[i] = cap
    #         else:
    #             print("摄像头不在线它的camera_id是:", self.camera_list[int(i)])
    #             self.clear_variable(i)  # 摄像头掉线，改变算法状态
    def saveImg(self,camera_id,i):
        print("保存图片...")
        save_dir = f"tea_action/{camera_id}/"
        print()
        if str(camera_id) in ["25","27","28","29","33","34","36","38","43","44","45","48"]:
            save_path = os.path.join(save_dir,f"front_{camera_id}_{self.frames}.jpg")

            # 创建保存目录
            os.makedirs(save_dir, exist_ok=True)
            cv2.imwrite(save_path, self.img1s[i])
        # if str(camera_id) in ["24", "26", "32"]:
        #     save_path = os.path.join(save_dir, f"back_{camera_id}_{self.frames}.jpg")
        #
        #     # 创建保存目录
        #     os.makedirs(save_dir, exist_ok=True)
        #     cv2.imwrite(save_path, self.img1s[i])

    def save_img(self, list_,i):
        # if(self.frames % 60 == 0):
        #     self.saveImg(self.camera_list[i], i)
        schedule.run_pending()
        # 当前时间
        now_localtime = time.strftime("%H:%M:%S", time.localtime())
        time_interval_one = Interval(config.get("server", "begin_time"), config.get("server", "end_time"))
        # 当前时间在这个区间就打开前摄像头消息推送和老师消息推送
        if now_localtime in time_interval_one:
            self.Level2sendSwitch = True
            self.teacher_mess_switch = True
        else:
            self.Level2sendSwitch = False
            self.teacher_mess_switch = False
        if (i < len(self.src_list)):
            if self.dataset.caps[i].isOpened():
                if len(self.img1s) > 0:
                    # param = str(self.camera_list[i]) + ":" + "name"
                    # print(f"--> {self.camera_list}")
                    # print(f"{i}-->",self.camera_list[i])
                    encoder = self.nj.encode(self.img1s[i])
                    # list_[i][self.camera_list[i]].put(encoder)
                    # print("890-->", list_[0]["25"].qsize())
                    # print("0k-->")
                    #
                    # if (list_[i].qsize() > 15):
                    #     list_[i].get()
                    # list_[i].put(encoder)
                    if list_.list2[i].qsize() >= 5:
                        list_.list2[i].get()
                    list_.list2[i].put(encoder)
                    del encoder
                else:
                    print("摄像头不在线它的camera_id是:", self.camera_list[int(i)])
                    cap = VideoCapture(self.src_list[i])
                    if cap == -1:
                        self.clear_variable(i)  # 摄像头掉线，改变算法状态
                    else:
                        self.dataset.caps[i] = cap
            else:
                print("摄像头不在线它的camera_id是:", self.camera_list[int(i)])
                self.clear_variable(i)  # 摄像头掉线，改变算法状态

    def cv2_chinese_text(self, img, text, left, top, text_size, text_color=(0, 255, 0)):
        # 判断是否OpenCV图片类型
        if (isinstance(img, np.ndarray)):
            img = Image.fromarray(cv.cvtColor(img, cv.COLOR_BGR2RGB))
        # 创建一个可以在给定图像上绘图的对象
        draw = ImageDraw.Draw(img)
        # 字体的格式
        font_style = ImageFont.truetype("./simsun.ttc",
                                        text_size, encoding="utf-8")
        # 绘制文本
        draw.text((left, top), text, text_color, font=font_style)
        # 转换回OpenCV格式
        return cv.cvtColor(np.asarray(img), cv.COLOR_RGB2BGR)

    # box标注
    def boxed_img(self, x, img, color=(0, 0, 255), label=None,show_names=True):
        tl = round(0.002 * (img.shape[0] + img.shape[1]) / 2) - 1  # line/font thickness
        p1, p2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))
        cv.rectangle(img, p1, p2, color, thickness=tl, lineType=cv.LINE_AA)

        if show_names:
            if label:
                tf = max(tl - 1, 1)  # font thickness
                # img = self.cv2_chinese_text(img, label, p1[0], p1[1] - 2, 20, color)
                cv.putText(img, label, (p1[0], p1[1] - 2), 0, (tl + 2) / 3, [225, 255, 255], thickness=tf,
                           lineType=cv.LINE_AA)

    def video_write(self, save_path, i):
        if not os.path.exists('static/save_fight_video'):
            os.mkdir('static/save_fight_video')
        w = self.img1s[i].shape[1]
        h = self.img1s[i].shape[0]
        file_path = os.path.join('static/save_fight_video', save_path)
        save_path = str(Path(file_path).with_suffix('.mp4'))
        self.vid_writer[i] = cv.VideoWriter(save_path, cv.VideoWriter_fourcc(*'mp4v'), 30,
                                            (w, h))
        for img in self.images[i]:
            self.vid_writer[i].write(img)
        self.vid_writer[i].release()
        self.images[i].clear()

    # 获取行为数量的函数   PearCode #
    def GetActionNumber(self, action, label_names,cameraID):
        self.Level2_num_list[cameraID][action] += 1
        return label_names.count(action)

    # 发送图片信息和数量的函数   PearCode #
    def Level2SendMsg(self, action_type, actionNumber, cameraID, PicturePath,
                      is_front=True):  # 根据 self.send_msg 函数改, cameraID是摄像头的id
        # action_data = {}
        if is_front:
            PicturePath = self.ip_stu + '/' + PicturePath
            # PicturePath = self.ip_stu + '/' + PicturePath
        else:
            PicturePath = self.ip_tea + '/' + PicturePath
            # PicturePath = self.ip_tea + '/' + PicturePath
        # path = self.ip_path+self.resutl_path
        # PicturePath = os.path.join(self.end_path, PicturePath)
        # print(PicturePath)
        if self.params[cameraID] is not None:
            # src_index = videoWriter_name.split('.')[0].split('-')[0][1:]
            # print("Level 2 alert is ready to be sent...")
            self.params[cameraID]["camera_id"] = self.camera_list[int(cameraID)]
            # print("551-->", self.camera_list[int(cameraID)])
            self.params[cameraID]["location"] = self.front
            self.params[cameraID]["created_at"] = self.get_current_time()
            a = str(u.uuid1()).split("\(")[0]
            self.params[cameraID]["data_id"] = a
            self.params[cameraID]["event_level"] = int(action_type / 10)  # 事件等级
            self.params[cameraID]["event_type"] = action_type
            # action_data["total_people"] = str(actionNumber)
            self.params[cameraID]["total_people"] = int(actionNumber)
            self.params[cameraID]["trajectory"] = []
            self.params[cameraID]["screenshot_url"] = PicturePath
            self.params[cameraID]["video_url"] = ""
            # self.thread_pool.submit(sendMessage, self.params[cameraID])
            util.sendMessage(self.params[cameraID])

    # 保存视频的函数
    def Level1SaveVideo(self, cameraID):
        print('-----------------正在保存视频---------------------')
        # self.videoWriter_name[cameraID] = video_name
        video_save_thread = Thread(target=self.video_write,
                                   args=(self.videoWriter_name[cameraID], cameraID))  # video_write2是重写的函数
        video_save_thread.start()
        print('-----------------视频保存完毕---------------------')

    # 安全等级为2的操作   PearCode #
    def Level2Function(self, label_names, cameraID, img, is_front=True):  # 安全等级为2，需根据人数变化推送图像与数量信息
        self.send_stu_message_lock.acquire()
        # if self.judgeNet():
        # print('Hello, This is Level2Function' + ' **' * 100)
        if not os.path.exists('static/stu_action_change'):
            os.mkdir('static/stu_action_change')
        action_list = ['sleep', 'phone', 'smoke', 'hand', 'stand']
        action_type = [21, 22, 23, 31, 32]  # 对应上面几种行为的事件类型序号
        # 1.判断人数是否变化
        for i, action in enumerate(action_list):
            # 1.1 分别获取新旧的动作数量
            newNumber = self.GetActionNumber(action, label_names,cameraID)
            oldNumber = self.Level2ActionNumberList[cameraID][i]
            # print('antion:{}  newNumber:{}  oldNumber:{}'.format(action, newNumber, oldNumber))
            diff = abs(newNumber - oldNumber)
            # 1.2 判断数量是否相等
            # if not newNumber == oldNumber:
            if diff >= 2 and self.Level2_num_list[cameraID][action] == 60:
                self.Level2_num_list[cameraID][action] = 0
                # print("573 --------------------------------------->")
                # 数量不相等则更新旧的动作数量，同时推送信息
                self.Level2ActionNumberList[cameraID][i] = newNumber
                # 2.推送信息
                # 2.1 准备图像信息，根据 resutl_save 函数的后半部分改
                # format_time = time.strftime("%Y-%m-%d %H-%M-%S", time.localtime())
                picture_name_time = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime())
                # video_name = 'num' + str(cameraID) + '-' + self.config.get('alarm_class', str(self.class_type[
                # cameraID])) + '-' + format_time
                PictureName = 'Camera' + str(self.camera_list[cameraID]) + '-' + action + '-' + picture_name_time
                # self.videoWriter_name[cameraID] = PictureName
                firstFrameName = PictureName + '.jpg'
                PicturePath = 'static/stu_action_change/' + firstFrameName
                cv.imwrite(PicturePath, img)
                # self.send_stu_message_lock.acquire()
                if newNumber != 0:
                    self.Level2SendMsg(action_type[i], newNumber, cameraID, PicturePath, is_front)
                    # pass
                else:
                    continue
        self.send_stu_message_lock.release()

    # 时间转换成帧数的函数-分钟转换成帧
    def Time2FPS(self, time):
        fps = 30  # 抽帧率
        return int(time * 60 * fps)

    # 时间转换成帧数的函数-秒转换成帧
    def Time2FPS2(self, time):
        fps = 30  # 抽帧率
        return int(time * fps)

    # 将报警的行为保存为视频
    # def save_images(self, image_list, prefix='image'):
    #     for i, image_array in enumerate(image_list):
    #         file_path = f"{prefix}_{i}.jpg"  # 文件路径，使用给定的前缀和索引
    #         cv.imwrite(file_path, image_array)

    # 安全等级为1，发送报警信息
    def Level1SendMsg(self, actionType, videlURL, PicUrl,
                      cameraID):  # actionType是各类行为对应的编号（11、12等）、videoWriter_name 视频名字
        # if self.judgeNet():
        # print('-------------------------------正在发送报警信息------------------------------')
        # path = self.ip_path + self.resutl_path
        # videlURL = self.ip_stu + '/' + videlURL
        # PicUrl = self.ip_stu + '/' + PicUrl
        videlURL = self.ip_stu + videlURL
        PicUrl = self.ip_stu + PicUrl

        # videlURL = os.path.join(self.end_path, videlURL)
        # PicUrl = os.path.join(self.end_path, PicUrl)
        if self.params[cameraID] is not None:
            # print("689-->",self.camera_list[int(cameraID)])
            self.params[cameraID]["camera_id"] = self.camera_list[int(cameraID)]
            self.params[cameraID]["location"] = self.front
            self.params[cameraID]["created_at"] = self.get_current_time()
            a = str(u.uuid1()).split("\(")[0]
            self.params[cameraID]["data_id"] = a
            self.params[cameraID]["event_level"] = 1  # 事件等级
            # self.params[cameraID]["event_type"] = self.class_name[cameraID]
            # print("事件类型：",self.class_name[cameraID])
            self.params[cameraID]["event_type"] = actionType
            # action_data["total_people"] = str(actionNumber)
            self.params[cameraID]["total_people"] = -1
            self.params[cameraID]["trajectory"] = []
            self.params[cameraID]["screenshot_url"] = PicUrl
            self.params[cameraID]["video_url"] = videlURL
            # self.thread_pool.submit(sendMessage, self.params[cameraID])
            util.sendMessage(self.params[cameraID])

    def save_images_as_video(self, image_list, output_file, fps=30.0):
        # 获取图像的尺寸
        height, width, _ = image_list[0].shape
        # 创建VideoWriter对象
        fourcc = cv.VideoWriter_fourcc(*'XVID')  # 使用XVID编码器
        video_writer = cv.VideoWriter(output_file, fourcc, fps, (width, height))
        # 将图像逐帧写入视频文件
        for image in image_list:
            # 转换图像数据类型为8位无符号整数
            image = np.uint8(image)
            # 将图像写入视频文件
            video_writer.write(image)
        # 释放VideoWriter对象
        video_writer.release()
        print("视频保存成功！")

    # 从列表里面的图片合成为一个视频并保存
    def from_list_save_video(self, frames, output_path):
        height, width, _ = frames[0].shape
        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        out = cv2.VideoWriter(output_path, fourcc, 30.0, (width, height))
        for frame in frames:
            out.write(frame)
        print("视频保存成功：", output_path)
        out.release()

    # 重写一级报警事件与保存视频的功能
    def TimerControl(self, label_names, cameraID):
        # print(f"摄像头id{cameraID},标签f{label_names}")
        action_list = ['fight', 'fall', 'fire', 'gather']  # new
        action_type = [11, 12, 13, 14]  # 对应上面几种行为的事件类型序号
        label_names = list(set(label_names))
        self.send_stu_message_lock.acquire()
        # print("cameraID==={}event====={}".format(cameraID,label_names))
        ret, action, img_0, video_img, send_message = self.trigger_alarm(cameraID, label_names, self.img1s[cameraID])
        self.send_stu_message_lock.release()
        if ret:
            self.class_name[cameraID] = action
            index = action_list.index(action)
            self.class_type[cameraID] = action_type[index]
            # 使用更新后的区域设置格式化时间time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime())
            # locale.setlocale(locale.LC_ALL, 'zh_CN.utf8')
            video_name_time = time.strftime("%Y-%m-%d_%H_%M_%S", time.localtime())
            output_dir = "static/" + self.class_name[cameraID] + '/'  # 保存的目录
            output_video_name = "camera" + str(self.camera_list[int(cameraID)]) + "_" \
                                + self.class_name[cameraID] + video_name_time + ".mp4"
            if video_img != None and len(video_img) > 0:
                print(len(video_img))
                Thread(target=self.from_list_save_video,
                       args=(video_img, os.path.join(output_dir, output_video_name))).start()
            # self.from_list_save_video(self.images[cameraID], os.path.join(output_dir, output_video_name))
            output_img_name = "camera" + str(self.camera_list[int(cameraID)]) + "_" \
                              + self.class_name[cameraID] + video_name_time + ".png"
            if send_message:
                cv.imwrite(os.path.join(output_dir, output_img_name), img_0)  # 保存第一帧的图片
                self.Level1SendMsg(actionType=self.class_type[cameraID],
                                   videlURL=os.path.join(output_dir, output_video_name),
                                   PicUrl=os.path.join(output_dir, output_img_name),
                                   cameraID=cameraID)

    def save_tea_trajectory_img(self, cameraID, img, pic_path):
        for point in self.points[cameraID]:
            cv.circle(img, (point[0], point[1]), 1, (255, 255, 255),
                      cv.FILLED)

        cv2.imwrite(pic_path, img)
        self.points[cameraID]=[]
        print(f"教师轨迹保存成功...{pic_path}")

    # 专门用于推送教师消息
    def send_tea_message(self, cameraID, names, img):
        # print("run_pending-------",self.send_tea_TSI)
        if self.send_tea_tw[cameraID]:
            if "TW" in names:
                self.send_tea_tw[cameraID] = False
                self.tea_tw(cameraID, names, img)
        if self.send_tea_lecture[cameraID]:
            if "lecture" in names:
                self.send_tea_lecture[cameraID] = False
                self.tea_lecture(cameraID, names, img)
        # 教师轨迹
        if self.send_tea_guiji[cameraID]:
            # print("推送相机ID为：",cameraID)
            self.send_tea_guiji[cameraID] = False
            if len(self.TMPPoints[cameraID]) > 0:
                self.teacher_guiji(cameraID, names, img)
        # 推送教师轨迹图片
        if self.send_tea_photo[cameraID] != False:
            # print("send_tea_photo===",self.send_tea_photo[cameraID])
            self.send_tea_photo[cameraID] = False
            self.tea_photo(cameraID, names, img)
            # print("send_tea_photo后===", self.send_tea_photo[cameraID])
        # 师生互动
        if self.send_tea_TSI[cameraID]:
            if "TSI" in names:
                self.send_tea_TSI[cameraID] = False
                self.tea_TSI(cameraID, names, img)
        if self.send_visitation[cameraID] != False:
            if 'visitation' in names:
                self.send_visitation[cameraID] = False
                self.tea_visitation(cameraID, names, img)

    # 专门用于推送听讲、教室人数
    def send_person(self, cameraID):
        # 听课人数
        if len(self.Listening[cameraID]) >= self.FramesListening and self.check[cameraID] == False:
            # print(f"1049:camera_id：{self.camera_list[int(cameraID)]},列表长度{len(self.Listening[cameraID])}\n\列表内容{self.Listening[cameraID]}")
            self.params[cameraID]["camera_id"] = self.camera_list[int(cameraID)]
            self.params[cameraID]["location"] = self.front
            self.params[cameraID]["created_at"] = self.get_current_time()
            a = str(u.uuid1())
            # a = a.split("-")[0]
            self.params[cameraID]["data_id"] = a
            self.params[cameraID]["event_level"] = 3  # 事件等级
            self.params[cameraID]["event_type"] = 33
            self.params[cameraID]["total_people"] = int(sum(self.Listening[cameraID]) / len(self.Listening[cameraID]))
            self.params[cameraID]["trajectory"] = []
            self.params[cameraID]["screenshot_url"] = ""
            self.params[cameraID]["video_url"] = ""
            # self.thread_pool.submit(sendMessage, self.params[cameraID])
            util.sendMessage(self.params[cameraID])
            self.Listening[cameraID] = []
        # 总人数
        # if len(self.people[cameraID]) >= self.FramesPeople and self.check[cameraID] == True:
        if len(self.people[cameraID]) >= self.FramesPeople:
            self.params[cameraID]["camera_id"] = self.camera_list[int(cameraID)]

            self.params[cameraID]["location"] = self.front
            self.params[cameraID]["created_at"] = self.get_current_time()
            a = str(u.uuid1()).split("\(")[0]
            self.params[cameraID]["data_id"] = a
            self.params[cameraID]["event_level"] = 3  # 事件等级
            self.params[cameraID]["event_type"] = 34
            # print("----->1074:",self.people[cameraID])
            self.params[cameraID]["total_people"] = int(sum(self.people[cameraID]) / len(self.people[cameraID]))
            self.params[cameraID]["trajectory"] = []
            self.params[cameraID]["screenshot_url"] = ""
            self.params[cameraID]["video_url"] = ""
            # self.thread_pool.submit(sendMessage, self.params[cameraID])
            util.sendMessage(self.params[cameraID])
            self.people[cameraID] = []
        # 读写人数
        if len(self.ReadWrite[cameraID]) >= self.FramesPeople and self.check[cameraID] == False:
            self.params[cameraID]["camera_id"] = self.camera_list[int(cameraID)]
            self.params[cameraID]["location"] = self.front
            self.params[cameraID]["created_at"] = self.get_current_time()
            a = str(u.uuid1()).split("\(")[0]
            self.params[cameraID]["data_id"] = a
            self.params[cameraID]["event_level"] = 3  # 事件等级
            self.params[cameraID]["event_type"] = 40
            self.params[cameraID]["total_people"] = int(sum(self.ReadWrite[cameraID]) / len(self.ReadWrite[cameraID]))
            self.params[cameraID]["trajectory"] = []
            self.params[cameraID]["screenshot_url"] = ""
            self.params[cameraID]["video_url"] = ""
            # self.thread_pool.submit(sendMessage, self.params[cameraID])
            util.sendMessage(self.params[cameraID])
            self.ReadWrite[cameraID] = []

    # 重要的函数!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    def resutl_save(self, cameraID, names, img, is_front=True):  # result是安全等级、i是摄像头的id

        if len(self.img1s) > 0:
            self.send_stu_message_lock.acquire()
            if hasattr(self, "people"):
                pepoles = 0
                Listenings = 0
                DWs = 0
                for item in names:
                    if item == "L":
                        Listenings += 1
                        pepoles += 1
                    elif item == "NL":
                        pepoles += 1
                    elif item == "DW":
                        DWs += 1

                self.people[cameraID].append(pepoles)
                self.Listening[cameraID].append(Listenings)

                if (DWs != 0):
                    self.ReadWrite[cameraID].append(DWs)
                # 每帧图片保存的人数
            if len(self.people[cameraID]) >= 0:
                if len(self.people[cameraID]) == 0:
                    self.check[cameraID] = True
                if self.Level2sendSwitch:
                    self.send_person(cameraID)
            self.send_stu_message_lock.release()
            if "fire" in names or "fall" in names or "gather" in names or "fight" in names:
                if int(config.get("server", "level_1_alarm_switch"))==1:
                    self.TimerControl(names, cameraID)
                # pass
            if "sleep" in names or "phone" in names or 'smoke' in names or 'hand' in names or 'stand' in names and self.Level2sendSwitch:  # PearCode 安全等级为2的时候需要发送图片、传动作数量
                self.Level2Function(names, cameraID, img, is_front)  # i是摄像头I
                # pass
    def read_img(self, img_array):
        swapped_array = np.transpose(img_array, (1, 2, 0))
        cv2.imshow("img", swapped_array)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    # 功能检测执行接口，获取画面，调用不同功能
    def execute_func(self,list_):
        # if self.frames == 30:
        #     self.start = time.time()
        # 开始时间
        cu_time=time.time()
        img, img0s = self.dataset.__next__()  # 0.05
        # print(f"{self.port}拉流消耗时间：",time.time()-cu_time) #30路拉满的时候0.2左右
        # self.read_img(img)
        # 列表：存放画了框的流画面;
        # self.img1s = copy.deepcopy(img0s)  # 深复制函数。所谓深复制，就是从输入变量完全复刻一个相同的变量，无论怎么改变新变量，原有变量的值都不会受到影响。 --Pear#
        self.img1s = img0s
        # 模型检测结果
        return_names = self.detect_stream(list_,img, img0s)
        # if self.frames == 1230:
        #     consume_time = time.time() - self.start
        #     print(f"端口号:{list_.port},对应的视频id{self.camera_list},1200帧图片检测时间为：{consume_time}")
        #     print(f"端口号:{list_.port},每帧检测时间为：{consume_time / (1200 * 1.0)}")
        #     print(f"端口号:{list_.port},对应的视频id{self.camera_list},12000帧消耗时间为:{(consume_time / (1200 * 1.0)) * 12000}")
        # return return_names  # self.img1s是原始图像, return_names：预测的动作列表

    def clear_variable(self, src_i):
        self.Level2ActionNumberList[src_i] = [0, 0, 0, 0,
                                              0]  # PearCode 对应['sleep', 'phone', 'hand', 'stand', 'smoke']的动作次数
        self.sort_id[src_i] = []
        self.k_time[src_i] = 0
        self.current_id[src_i] = 9999
        self.points[src_i] = []
        self.class_type[src_i] = 99
        self.vid_writer[src_i] = None
        self.videoWriter_name[src_i] = ''
        self.img1s[src_i] = []
        self.Time_i = 0

        # 一级报警相关  重置
        self.last_triggered[src_i] = {}
        self.window[src_i] = []
        self.switch_window[src_i] = {'fight': True, 'fall': True, 'fire': True, 'gather': True}
        self.trigger_img[src_i] = {'fight': [], 'fall': [], 'fire': [], 'gather': []}
        self.first_trigger_img[src_i] = {}
        self.switch_video[src_i] = {}
        self.switch[src_i] = {'fight': None, 'fall': None, 'fire': None, 'gather': None}
        self.send_switch[src_i] = {'fight': None, 'fall': None, 'fire': None, 'gather': None}
        self.num_list[src_i] = {'fight': 0, 'fall': 0, 'fire': 0, 'gather': 0}
        self.trigger_save_img[src_i] = {'fight': True, 'fall': True, 'fire': True, 'gather': True}
        self.have_save_video[src_i] = {'fight': False, 'fall': False, 'fire': False, 'gather': False}

    # 发送教师轨迹图片 --> 胡瑞
    def pointspicture_sengmessage(self, cameraID, PicturePath):
        PicturePath = self.ip_stu + '/' + PicturePath
        # PicturePath = os.path.join(self.end_path, PicturePath)
        if self.judgeNet():
            if self.params[cameraID] is not None:
                # src_index = videoWriter_name.split('.')[0].split('-')[0][1:]
                print("Level 2 alert is ready to be sent...")
                self.params[cameraID]["camera_id"] = self.camera_list[int(cameraID)]
                a = str(u.uuid1()).split("\(")[0]
                self.params[cameraID]["data_id"] = a
                self.params[cameraID]["event_level"] = 3  # 事件等级
                self.params[cameraID]["event_type"] = 36
                self.params[cameraID]["person_type"] = 2
                # action_data[ActionName] = str(actionNumber)
                self.params[cameraID]["video_url"] = ""
                self.params[cameraID]["screenshot_url"] = PicturePath
                format_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
                self.params[cameraID]["created_at"] = format_time
                print('本次推送的是教师的轨迹图片:', self.params[cameraID])
                # self.thread_pool.submit(sendMessage, self.params[cameraID])
                message = util.sendMessage(param=self.params[cameraID])
            else:
                print("网络已中断，请检测网络连接................")

    def send_points(self):
        if not os.path.exists('static/results'):
            os.mkdir('static/results')
        for i in range(self.src_num):
            save_image = self.img1s[i]
            for point in self.points[i]:
                cv.circle(save_image, (point[0], point[1]), 1, (255, 255, 255), cv.FILLED)
            # video_name = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime())
            video_name = 'camera' + str(self.camera_list[int(i)]) + '-' + 'teacherTrack'
            firstFrameName = video_name + '.jpg'
            PicturePath = './results/' + firstFrameName
            cv.imwrite(PicturePath, save_image)
            self.pointspicture_sengmessage(i, firstFrameName)
        self.points = [[] for x in range(self.src_num)]